/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package deadlocksim.backend.resources;
import deadlocksim.*;
import deadlocksim.backend.*;
import deadlocksim.backend.process.*;

import java.util.*;

/**
 *
 * @author romilian
 */
public class ResourceManager 
{
    private Map<DeadlockProcess, LinkedList<ProcessResource>> resourcesUsedMap;
    private Map<DeadlockProcess, LinkedList<ResourceRequest>> resourcesWantedMap;
    private Map<Integer, LinkedList<ProcessResource>> resourcesAvailableMap;
    private Map<Integer, Integer> resourcesTotalMap;
    
    // For the bankers algorithm.
    //  For each process, this tracks the maximum number of each resource
    //  the process can hold at once.
    //  This is calculated taking into account a process freeing resources
    //      (thereby reducing it's maximum)
    //  Example, if P1 requests R0, frees R0, and then requests R0, and then frees R0
    //      and never touches R0 again, the entry for P1, R0 == 1 forever
    //private Map<DeadlockProcess, Map<Integer, Integer>> resourcesMaxMap;
    
    
    private SimClock clock;
    private DeadlockSim mySim;
    
    private boolean bankersDeadlockAvoidanceMode;
    
    public ResourceManager(DeadlockSim sim)
    {
        mySim = sim;
        clock = mySim.getSimClock();
        resourcesUsedMap = new HashMap<DeadlockProcess, LinkedList<ProcessResource>>();
        resourcesWantedMap = new HashMap<DeadlockProcess, LinkedList<ResourceRequest>>();
        resourcesAvailableMap = new HashMap<Integer, LinkedList<ProcessResource>>();
        resourcesTotalMap = new HashMap<Integer, Integer>();
        
        //resourcesMaxMap = new HashMap<DeadlockProcess, Map<Integer, Integer>>();
        bankersDeadlockAvoidanceMode = false;
    }
    
    public void registerProcess(DeadlockProcess p)
    {
        //this.storeMaxResources(p);
    }
    public void deregisterProcess(DeadlockProcess p)
    {
        freeAllFromProcess(p);
        removeResourceRequests(p);
        //this.removeMaxResources(p);
    }
    // Reset current process info so it can run again anew
    public void resetProcess(DeadlockProcess p)
    {
        deregisterProcess(p);
        //this.storeMaxResources(p);
    }
    
    private Map<Integer, Integer> copyMap(Map<Integer, Integer> mapToCopy)
    {
        Map<Integer, Integer> newMap = new HashMap<Integer, Integer>();
        if(mapToCopy == null)
            return newMap;
        Iterator<Integer> keys = mapToCopy.keySet().iterator();
        while(keys.hasNext())
        {
            int key = keys.next();
            newMap.put(key, mapToCopy.get(key).intValue());
        }
        return newMap;
    }
    // Support for bankers algorithm
    /*
    private void storeMaxResources(DeadlockProcess p)
    {
        resourcesMaxMap.put(p, p.countMaxResources());
    }
    // support for bankers algorithm
    private void removeMaxResources(DeadlockProcess p)
    {
        resourcesMaxMap.remove(p);
    }
     */
    // support for bankers algorithm
    //  Remove a resource from the max a process needs
    //  This should be called when a resource is granted
    /*
    public void decrementFromMaxNeeded(DeadlockProcess p, int resourceID)
    {
        if(! resourcesMaxMap.containsKey(p))
            return; // do nothing
        Map<Integer, Integer> pMaxNeededMap = resourcesMaxMap.get(p);
        if(pMaxNeededMap == null)
            return; // do nothing
        if(! pMaxNeededMap.containsKey(resourceID))
            return; // do nothing
        int newVal = pMaxNeededMap.get(resourceID) - 1;
        if(newVal < 0)
            return; // Do nothing; can't have negative max resources
        else
            pMaxNeededMap.put(resourceID, newVal); // value can be decremented; do it.
    }
     */
    
    // core logic for bankers algorithm
    //  Returns true if the resource request of resourceID by p would result 
    //  in a safe state if satisfied
    // Returns false if the request is impossible (no resources, etc.) or if the
    //  resulting state is unsafe
    public boolean isSafeState(DeadlockProcess proc, int resourceID)
    {
        LinkedList<DeadlockProcess> markedAsTerminated = new LinkedList<DeadlockProcess>();
        //Set<DeadlockProcess> unmarkedProcs = resourcesWantedMap.keySet();
        java.util.List<DeadlockProcess> unmarkedProcs = mySim.getProcesses();
        
        // A copy of the resourcesAvailableMap that we can work with
        Map<Integer, Integer> availMap = this.getAvailableResourcesByType();
        if(!availMap.containsKey(resourceID) || availMap.get(resourceID) <= 0)
            return false; // no resource to grant
        
        
        // To create the state under consideration:
        //      remove the resource from the availMap (our local copy)
        //      add the resource to the used map (as owned by P) (local copy)
        //      reduce the max needed by P
        availMap.put(resourceID, availMap.get(resourceID)-1);
        
        Map<Integer, Integer> usedByPMap = this.getAssociatedResourcesByType(proc, resourcesUsedMap);
        if(usedByPMap == null)
            usedByPMap = new HashMap<Integer, Integer>();
        
        Integer numUsedByP = usedByPMap.get(resourceID);
        if(numUsedByP != null)
            usedByPMap.put(resourceID, numUsedByP + 1);
        else
            usedByPMap.put(resourceID, 1);
        
        Map<Integer, Integer> maxNeededByPMap = proc.countMaxResources();//copyMap(this.resourcesMaxMap.get(proc));
        Integer maxNeededOfRID = maxNeededByPMap.get(resourceID);
        if(maxNeededOfRID == null || maxNeededOfRID <= 1)
            maxNeededByPMap.put(resourceID, 0);
        else
            maxNeededByPMap.put(resourceID, maxNeededByPMap.get(resourceID) - 1);
        
        
        // Default to true (even though it's not) just to get going
        boolean processMarkedLastIter = true; // A process was marked as terminated the last pass through
        
        // If there are still unmarked processes, and we haven't already gone
        //      through the same list without marking
        while(unmarkedProcs.size() > 0 && processMarkedLastIter)
        {
            // Unmarked processesl; these might be deadlocked
            Iterator<DeadlockProcess> unmarkedProcsIter = unmarkedProcs.iterator();
           
            // We'll set this to true if we mark something could finish 
            //      execution using all available resources
            processMarkedLastIter = false;
            
            // Iterate through the ones that might be able to terminate using
            //  available resources
            while(unmarkedProcsIter.hasNext())
            {
                DeadlockProcess nextP = unmarkedProcsIter.next();
                
                Map<Integer, Integer> maxNeededByNextP;
                if(nextP.equals(proc))
                    // Use the special map we set asside to represent the proposed change
                    maxNeededByNextP = maxNeededByPMap; 
                else
                    maxNeededByNextP = nextP.countMaxResources();
                
                Map<Integer, Integer> allocatedToP;
                if(nextP.equals(proc))
                    // Use the special map we set asside to represent the proposed change
                    allocatedToP = usedByPMap; 
                else
                    allocatedToP = this.getAssociatedResourcesByType(nextP, resourcesUsedMap);
                //Map<Integer, Integer> allocatedToP  = this.getAssociatedResourcesByType(nextP, this.resourcesUsedMap);
                
                // If the resources this guy wants over the course of its life 
                //  are all available
                if(maxNeededByNextP == null || this.requestedLessThanOrEqualToAvailable(maxNeededByNextP, availMap))
                {
                    // We found a guy that isn't deadlocked yet
                    processMarkedLastIter = true;
                    
                    
                    // Pretend that this guy finishes; add his allocation to the
                    /// "available" pool
                    if(allocatedToP != null)
                    {
                        // Some resources are allocated to P; add them to 
                        //  the available resources to simulate it's completion
                        addMapToMap(allocatedToP, availMap);
                    }
                    //Mark this guy as terminated
                    markedAsTerminated.add(nextP);
                }
                else
                {
                    // This guy might be deadlocked; we'll see when the algorithm finishes
                    // Don't mark it
                    continue;
                }
            }
            
            // Remove any processes that have not been marked
            unmarkedProcs.removeAll(markedAsTerminated);
            
            // Keep going until we have a list of unmarked processes that we
            // have already checked.
        }
        if(unmarkedProcs.size() > 0)
        {
            
            // The remaining unmarked list will all be deadlocked
            mySim.sendMessage("Deadlock Avoidance System: Unsafe state detected; denying resource request", false);
            return false;
        }
        else
        {
            // Return an empty list
            
            mySim.sendMessage("Deadlock Avoidance System: Safe state detected; granting resource request", false);
            return true;
        }
    }
    
    
    public void createResource(int typeID)
    {
        ProcessResource newResource = new ProcessResource(typeID);
        Integer resourcesOfTypeID = resourcesTotalMap.get(typeID);
        if(resourcesOfTypeID == null || resourcesOfTypeID == 0)
        {
            
            resourcesTotalMap.put(typeID, 1);
        }
        
        addResourceToAvail(newResource);
    }
    
    // Returns true if there was a resource to free
    // Returns false if there was no available resource to free
    public boolean deleteFreeResource(int typeID)
    {
        int typeIDAvail = this.getNumAvail(typeID);
        if(typeIDAvail > 0)
        {
            mySim.sendMessage("Deleting 1 resource R" + typeID 
                        +" from simulator!",false);
            int typeIDTotal = resourcesTotalMap.get(typeID);
            
            resourcesTotalMap.put(typeID, typeIDTotal - 1);
            ProcessResource similarResource = new ProcessResource(typeID);
            resourcesAvailableMap.get(typeID).remove(similarResource);
            return true;
        }
        return false;
        
            
    }
    
    public void destroyResourceType(int typeID)
    {
        ProcessResource similarResource = new ProcessResource(typeID);
        
        // Free from usedResources
        this.takeAllOfType(typeID);
        resourcesAvailableMap.remove(typeID);
        //this.resourcesAvailableMap.put(typeID, null);
        this.resourcesTotalMap.put(typeID, 0);
        
    }
    
    public LinkedList<DeadlockProcess> getDeadlockedProcesses()
    {
        LinkedList<DeadlockProcess> markedAsTerminated = new LinkedList<DeadlockProcess>();
        //Set<DeadlockProcess> unmarkedProcs = resourcesWantedMap.keySet();
        java.util.List<DeadlockProcess> unmarkedProcs = mySim.getProcesses();
        
        // A copy of the resourcesAvailableMap that we can work with
        Map<Integer, Integer> availMap = this.getAvailableResourcesByType();

        
        // Default to true (even though it's not) just to get going
        boolean processMarkedLastIter = true; // A process was marked as not deadlocked the last pass through
        
        // If there are still unmarked processes, and we haven't already gone
        //      through the same list without marking
        while(unmarkedProcs.size() > 0 && processMarkedLastIter)
        {
            // Unmarked processesl; these might be deadlocked
            Iterator<DeadlockProcess> unmarkedProcsIter = unmarkedProcs.iterator();
           
            // We'll set this to true if we mark something as "not deadlocked"
            processMarkedLastIter = false;
            
            // Iterate through the ones that might be deadlocked
            while(unmarkedProcsIter.hasNext())
            {
                DeadlockProcess p = unmarkedProcsIter.next();
                Map<Integer, Integer> requestedByP    = getRequestsByResourceType(p);
                Map<Integer, Integer> allocatedToP    = this.getAssociatedResourcesByType(p, resourcesUsedMap);
                
                // If the resources this guy wants are all available
                if(requestedByP == null || this.requestedLessThanOrEqualToAvailable(requestedByP, availMap))
                {
                    // We found a guy that isn't deadlocked yet
                    processMarkedLastIter = true;
                    
                    // Pretend that this guy finishes; add his allocation to the
                    /// "available" pool
                    if(allocatedToP != null)
                        // Some resources are allocated to P
                        addMapToMap(allocatedToP, availMap);
                    //Mark this guy as not deadlocked
                    markedAsTerminated.add(p);
                }
                else
                {
                    // This guy might be deadlocked; we'll see when the algorithm finishes
                    // Don't mark it
                    continue;
                }
            }
            // Remove any processes that have not been marked
            unmarkedProcs.removeAll(markedAsTerminated);
            
            // Keep going until we have a list of unmarked processes that we
            // have already checked.
        }
        if(unmarkedProcs.size() > 0)
        {
            
            // The remaining unmarked list will all be deadlocked
            return new LinkedList<DeadlockProcess>(unmarkedProcs);
        }
        else
        {
            // Return an empty list
            return new LinkedList<DeadlockProcess>();
        }
        
    }
    
    public LinkedList<Integer> getResourceTypes() //java.util.List<Integer> getResourceTypes()
    {
        return new LinkedList<Integer>(this.resourcesAvailableMap.keySet());
    }
    
    private void addMapToMap(Map<Integer, Integer> from, Map<Integer, Integer> to)
    {
        Iterator<Integer> keySetIter = from.keySet().iterator();
        while(keySetIter.hasNext())
        {
            Integer nextKey = keySetIter.next();
            Integer fromVal = from.get(nextKey);
            Integer toVal = to.get(nextKey);
            if(toVal == null)
            {
                to.put(nextKey, fromVal);
            }
            else
            {
                to.put(nextKey, fromVal + toVal);
            }
        }
    }
    
    private Map<Integer, Integer> getAvailableResourcesByType()
    {
        Map<Integer, Integer> availByType = new HashMap<Integer, Integer>();
        Set<Integer> keySet = resourcesAvailableMap.keySet();
        Iterator<Integer> typeIter = keySet.iterator();
        while(typeIter.hasNext())
        {
            Integer type = typeIter.next();
            Integer typeCount = resourcesAvailableMap.get(type).size();
            availByType.put(type, typeCount);
        }
        return availByType;
    }
    
    // This counts up lists of resources by type and puts them in a map
    // This is a helper method for the Deadlock Analyzer
    private Map<Integer, Integer> getAssociatedResourcesByType(DeadlockProcess p, 
                                                                Map<DeadlockProcess, LinkedList<ProcessResource>> map)
    {
        LinkedList<ProcessResource> pAssociated = map.get(p);
        if(pAssociated == null)
        {
            return null;
        }
        else
        {
            HashMap<Integer, Integer> associatedByType = new HashMap<Integer, Integer>();
            Iterator<ProcessResource> associatedIter = pAssociated.iterator();
            while(associatedIter.hasNext())
            {
                ProcessResource nextResource = associatedIter.next();
                int rID = nextResource.getTypeId();
                if(associatedByType.containsKey(nextResource))
                {
                    
                    // Increment the number of this type
                    associatedByType.put(rID, associatedByType.get(rID) + 1);
                }
                else
                {
                    // Add the first of this type
                    associatedByType.put(rID, 1);
                }
            }
            return associatedByType;
        }
    }
    
    public boolean getBankersMode()
    {
        return this.bankersDeadlockAvoidanceMode;
    }
    private Map<Integer, Integer> getRequestsByResourceType(DeadlockProcess p)
    {
        LinkedList<ResourceRequest> pRequests = resourcesWantedMap.get(p);
        
        if(pRequests == null)
        {
            return null;
        }
        else
        {
            
            
            HashMap<Integer, Integer> requestsByType = new HashMap<Integer, Integer>();
            // This approach is wasteful
            // getNumWanted should only be called once per resource type
            Iterator<ResourceRequest> requestIter = pRequests.iterator();
            while(requestIter.hasNext())
            {
                ResourceRequest nextRequest = requestIter.next();
                int rID = nextRequest.resourceID;
                requestsByType.put(rID, this.getNumWanted(p, rID));
                /*
                if(requestsByType.containsKey(nextRequest))
                {
                    
                    // Increment the number of this type
                    requestsByType.put(rID, requestsByType.get(rID) + 1);
                }
                else
                {
                    // Add the first of this type
                    requestsByType.put(rID, 1);
                }
                 * */
                
            }
            return requestsByType;
        }
    }
    
    // Returns:
    //  true if 
    private boolean requestedLessThanOrEqualToAvailable(Map<Integer, Integer> requestedMap, 
                                                        Map<Integer, Integer> availableMap)
    {
        Iterator<Integer> requestedTypeIter = requestedMap.keySet().iterator();
        while(requestedTypeIter.hasNext())
        {
            Integer nextType = requestedTypeIter.next();
            Integer amountRequested = requestedMap.get(nextType);
            if(amountRequested == null || amountRequested == 0)
                continue;
            else if(! availableMap.containsKey(nextType))
                // None available of the type requested; more requested than available
                return false;
            else
            {
                Integer amountAvailable = availableMap.get(nextType);
                if(amountRequested <= amountAvailable)
                {
                    continue;
                }
                else
                {
                    return false;
                }
            }
        }
        // Made it to the end, and each requested was <= the amount available
        return true;
    }
    
    public void addResourceToAvail(ProcessResource r)
    {
        LinkedList<ProcessResource> resourcesOfTypeID = resourcesAvailableMap.get(r.typeID);
        if(resourcesOfTypeID == null)
        {
            resourcesOfTypeID = new LinkedList<ProcessResource>();
            resourcesAvailableMap.put(r.typeID, resourcesOfTypeID);
            //resourcesTotalMap.put(r.typeID, 0);
        }
        // Get the LinkedList associated with this resource type
        //  Create a new resource of that type, and add it to the LinkedList
        resourcesOfTypeID.add(r);
    }

    public void freeResource(DeadlockProcess p, int resourceTypeID) 
    {
        // Eliminate a resource of the specified type from p's allocation
        
        // Make a similar resource so that resourceList knows what we want
        ProcessResource similarResource = new ProcessResource(resourceTypeID);
        LinkedList<ProcessResource> resourceList = resourcesUsedMap.get(p);
        
        // Find it in the list of allocated resources
        int indexOfResource = resourceList.indexOf(similarResource);
        // Remove the one in the list that is equivalent to similarResource
        if(indexOfResource >= 0)
            similarResource = resourceList.remove(indexOfResource);
        else
            return;
        
        // Add back to the pool
        addResourceToAvail(similarResource);
    }
    
    public void takeAllOfType(DeadlockProcess p, int resourceTypeID)
    {
        mySim.sendMessage("Freeing ALL R" + resourceTypeID
                            + " resources from process P" + p.getID() +"!",false);
        int numUsedByP = this.getNumUsed(p, resourceTypeID);
        for(int i = 0; i < numUsedByP; i++)
            takeResource(p, resourceTypeID);
    }
    public void takeAllOfType(int resourceTypeID)
    {
        mySim.sendMessage("Freeing all resources of type R" 
                        + resourceTypeID +"!",false);
        LinkedList<DeadlockProcess> processSet = this.getUsingProcesses();
        Iterator<DeadlockProcess> processIter = processSet.iterator();
        while(processIter.hasNext())
        {
            takeAllOfType(processIter.next(), resourceTypeID);
        }
    }
    
    // Don't let p request again
    public void freeAllFromProcess(DeadlockProcess p)
    {
        LinkedList<ProcessResource> rListOriginal =this.resourcesUsedMap.get(p);
        if(rListOriginal == null)
            return;
        LinkedList<ProcessResource> rList = new LinkedList<ProcessResource>(rListOriginal);
        if(rList == null)
            return;
        Iterator<ProcessResource> rIter = rList.iterator();
        while(rIter.hasNext())
        {
            this.freeResource(p, rIter.next().typeID);
        }
    }
    public void takeAllFromProcess(DeadlockProcess p)
    {
        LinkedList<ProcessResource> rListOriginal =this.resourcesUsedMap.get(p);
        if(rListOriginal == null)
            return;
        LinkedList<ProcessResource> rList = new LinkedList<ProcessResource>(rListOriginal);
        if(rList == null)
            return;
        Iterator<ProcessResource> rIter = rList.iterator();
        mySim.sendMessage("Freeing resources from process P" + p.getID(), false);
        while(rIter.hasNext())
        {
            this.takeResource(p, rIter.next().typeID);
        }
    }
    
    public int getNumOfType (int typeId)
    {
        Integer returnInt = resourcesTotalMap.get(typeId);
        if(returnInt == null)
            return 0;
        else
            return returnInt;
    }
    
    public LinkedList<DeadlockProcess> getUsingProcesses()
    {
        return new LinkedList<DeadlockProcess>(this.resourcesUsedMap.keySet());
    }
    public LinkedList<DeadlockProcess> getWantingProcesses()
    {
        return new LinkedList<DeadlockProcess>(this.resourcesWantedMap.keySet());
    }
    public int getNumAvail(int resourceID)
    {
        LinkedList<ProcessResource> rList = this.resourcesAvailableMap.get(resourceID);
        if(rList == null)
            return 0;
        else
            return rList.size();
    }
    public int getNumUsed(int resourceID)
    {
        int numUsed = 0;
        Iterator<DeadlockProcess> pIter = this.getUsingProcesses().iterator();
        while(pIter.hasNext())
        {
            numUsed = numUsed + getNumUsed(pIter.next(), resourceID);
        }
        return numUsed;
    }
    public int getNumUsed (DeadlockProcess p, int resourceId)
    {
        int numUsed = 0;
        LinkedList<ProcessResource> resourceList = resourcesUsedMap.get(p);
        if(resourceList == null)
            return 0;
        
        Iterator<ProcessResource> resourceIter = resourceList.iterator();
        ProcessResource nextResource;
        
        while(resourceIter.hasNext())
        {
            nextResource = resourceIter.next();
            if (resourceId == nextResource.getTypeId())
                numUsed++;
        }
        return numUsed;
    }
    
    public int countResourceRequests(DeadlockProcess p, int resourceID)
    {
        int numWanted = 0;
        LinkedList<ResourceRequest> resourceList = resourcesWantedMap.get(p);
        if(resourceList == null)
            return 0;
        
        Iterator<ResourceRequest> resourceIter = resourceList.iterator();
        ResourceRequest nextResource;
        
        while(resourceIter.hasNext())
        {
            nextResource = resourceIter.next();
            if (resourceID == nextResource.resourceID)
                numWanted++;
        }
        return numWanted;
    }
    public int getNumWanted (DeadlockProcess p, int resourceId)
    {
        if(countResourceRequests(p, resourceId) > 0)
            return 1;
        else
            return 0;
    }
    public void removeResourceRequests(DeadlockProcess p)
    {
        resourcesWantedMap.put(p, null);
    }
    public void removeResourceRequest(DeadlockProcess p, int typeID)
    {
        LinkedList<ResourceRequest> requests = resourcesWantedMap.get(p);
        if(requests == null)
        {
            // No requests stored, nothing to do
            return;
        }
        
        int nullInt = 0; // The int doesn't matter, similarRequest is a dummy
        // By storing the time of the request, we can help resolve priority later
        ResourceRequest similarRequest = new ResourceRequest(nullInt, typeID);
        while(requests.contains(similarRequest))
            // Remove all requests for this resource
            requests.remove(similarRequest);
        
    }
    //public ProcessResource requestResource(int typeID)
    public ProcessResource requestResource(DeadlockProcess p, int typeID)
    {
        boolean isAvailable = true;
        ProcessResource similarResource = new ProcessResource(typeID);
        
        /* // Print available resources
        Set<Integer> keySet = resourcesAvailableMap.keySet();
        Iterator resourceTypeIter = keySet.iterator();
        while(resourceTypeIter.hasNext())
        {
            Integer resourceType = (Integer)resourceTypeIter.next();
            LinkedList<ProcessResource> resourceList
                    = resourcesAvailableMap.get(resourceType);
            System.out.println("Resource type: " + resourceType + " Number Avail: =" + resourceList.size()); 
        }
        */
        LinkedList<ProcessResource> availableOfTypeID 
                = resourcesAvailableMap.get(typeID);
        if(availableOfTypeID == null)
        {
            mySim.sendMessage("List of available resources is null ResourceType: " + typeID, true);
            isAvailable = false;
        }
        else if(! availableOfTypeID.contains(similarResource))
        {
            isAvailable = false;
        }
        
            // Default to NO!
        boolean shouldGrantToP = false;
        
        if(!isAvailable)
        {
            shouldGrantToP = false;
        }
        else if(this.bankersDeadlockAvoidanceMode)
        {
            shouldGrantToP = this.isSafeState(p, typeID);
        }
        else
        {
            // See whether this process has wanted it the most
            int wantedByP = this.countResourceRequests(p, typeID);
            Iterator<DeadlockProcess> pIter = this.getWantingProcesses().iterator();
            int mostWanted = 0;
            while(pIter.hasNext())
            {
                DeadlockProcess tempProc = pIter.next();
                if(tempProc.equals(p))
                    continue; // Want to evaluate everyone BUT p
                int wantedByTemp = countResourceRequests(tempProc, typeID);
                if(wantedByTemp > mostWanted)
                    mostWanted = wantedByTemp;
            }
            if(wantedByP == mostWanted)
            {
                shouldGrantToP = true;
                /*
                // Could go either way; decide randomly
                java.util.Random randomGen = new Random();
                float randomVal = randomGen.nextFloat();
                if(randomVal >= .5)
                    shouldGrantToP = true; // give the resource to P
                else
                    shouldGrantToP = false;
                 * */
            }
            else if(wantedByP < mostWanted)
                shouldGrantToP = false;
            else if(wantedByP > mostWanted)
                shouldGrantToP = true;
        }
        if(shouldGrantToP)
        {
            // Transfer the resource from "available" to "used"
            int indexOfResource = availableOfTypeID.indexOf(similarResource);
            similarResource = availableOfTypeID.remove(indexOfResource);
            
            // Record the allocation
            storeResourceAllocation(p, similarResource);

            // Remove records of unsatisfied requests
            removeResourceRequest(p, typeID);

            // Allocation successful; provide the resource
            return similarResource;
        }
        else
        {
            //("Resource of type " + typeID + " is not available.");
            storeResourceRequest(p, typeID);
            return null;
        }
    }
    
    public void setBankersDeadlockAvoidance(boolean bankersOn)
    {
        this.bankersDeadlockAvoidanceMode = bankersOn;
    }
    
    public void storeResourceAllocation(DeadlockProcess p, ProcessResource r)
    {
        LinkedList<ProcessResource> resourcesAllocatedToP = resourcesUsedMap.get(p);
        if(resourcesAllocatedToP == null)
        {
            // None allocated to P; Initialize list for the first time
            resourcesAllocatedToP = new LinkedList<ProcessResource>();
            resourcesUsedMap.put(p, resourcesAllocatedToP);
        }
        resourcesAllocatedToP.add(r);
    }
    public void takeResource(DeadlockProcess p, int typeID)
    {
        this.freeResource(p, typeID);
        
        // Give the process the ability to get it back!
        RequestResourceAction requestA = new RequestResourceAction(typeID);
        p.interruptWithAction(requestA);
    }
    public void storeResourceRequest(DeadlockProcess p, int typeID)
    {
        LinkedList<ResourceRequest> requests = resourcesWantedMap.get(p);
        if(requests == null)
        {
            requests = new LinkedList<ResourceRequest>();
            resourcesWantedMap.put(p, requests);
        }
        
        // By storing the time of the request, we can help resolve priority later
        ResourceRequest thisRequest = new ResourceRequest(clock.getTime(), typeID);
        requests.add(thisRequest);
    }
    
    
    private class ResourceRequest
    {
        public int time;
        public int resourceID;
        
        public ResourceRequest(int aTime, int aResourceID)
        {
            time = aTime;
            resourceID = aResourceID;
        }
        public boolean equals(Object o)
        {
            // ResourceRequests are the same if the typeIDs are the same
            if(o instanceof ResourceRequest 
                    && ((ResourceRequest)o).resourceID == resourceID)
                return true;
            else
                return false;
        }
    }

}
